package persistence;

import java.util.Collection;
import java.util.Vector;

import junit.framework.Assert;
import junit.framework.TestCase;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import persistence.hibernate.HibernatePersistenceController;
import persistence.repository.Repository;
import util.NotFoundException;

/**
 * @author idan
 */
public class RepositoryTest extends TestCase
{
	private static class DummyRepository extends Repository<Dummy>
	{
		public DummyRepository(final PersistenceController persistence)
		{
			super(persistence) ;
		}

		public Dummy create(final String name)
		{
			Dummy dummy = new Dummy(name) ;
			this.put(dummy) ;
			return dummy ;
		}
	}

	private static final String PATH = Definitions.makePath(Definitions.DATA_DIRECTORY, RepositoryTest.class) ;
	private static final String MAIN_REPO_URI = RepositoryTest.PATH + "dummies.xml" ;

	private DummyRepository mainRepo ;
	private static SessionFactory sessionFactory ;

	private static PersistenceController newPersistence(final boolean load) throws CorruptDataException
	{
		//return new XStreamPersistenceController(RepositoryTest.MAIN_REPO_URI, load) ;
		return new HibernatePersistenceController(Dummy.class, load, RepositoryTest.sessionFactory) ;
	}

	public RepositoryTest()
	{
		Configuration config = new Configuration() ;
		RepositoryTest.sessionFactory = config.configure().buildSessionFactory() ;
	}


	/**
	 * Test method for {@link persistence.repository.Repository#Repository(persistence.PersistenceController)}.
	 * Test method for {@link persistence.repository.Repository#close()}.
	 */
	public void testConstructorsDestructors()
	{
		// add an element to see if the constructor enables correct load from the URI.
		Dummy dummy31 = this.mainRepo.create("dummy31") ;
		this.mainRepo.flush() ;
		Dummy dummy32 = null ;

		DummyRepository mainRepoClone ;
		try
		{
			PersistenceController persistence = RepositoryTest.newPersistence(PersistenceController.LOAD) ;

			mainRepoClone = new DummyRepository(persistence) ;
			Assert.assertTrue(mainRepoClone.contains(dummy31.getId())) ;

			// add an element to verify that the constructor enables correct save to the URI.
			dummy32 = mainRepoClone.create("dummy32") ;
			mainRepoClone.close() ; // also tests that 'close' saves to persistence.
		}
		catch (final CorruptDataException e)
		{
			Assert.fail("database not created properly by constructor of \"this.repo\"") ;
		}

		try
		{
			PersistenceController persistence = RepositoryTest.newPersistence(PersistenceController.LOAD) ;

			mainRepoClone = new DummyRepository(persistence) ;

			// also checks that the initial set of values is the one loaded from the URI.
			Assert.assertTrue(mainRepoClone.contains(dummy32.getId())) ;
		}
		catch (final CorruptDataException e)
		{
			Assert.fail("database not saved properly") ;
		}
	}


	/**
	 * Test method for {@link persistence.repository.Repository#contains(long)}.
	 */
	public void testContains()
	{
		Assert.assertFalse(this.mainRepo.contains(-1)) ;


		final Vector<Dummy> dummyArray = this.makeDummies(41, 44) ;

		for (final Dummy element: dummyArray)
		{
			Assert.assertTrue(this.mainRepo.contains(element.getId())) ;
		}
	}

	/**
	 * Test method for {@link persistence.repository.Repository#destroy(long)}.
	 */
	public void testDestroy()
	{
		Dummy dummy60 = this.mainRepo.create("dummy60") ;
		try
		{
			Assert.assertTrue(this.mainRepo.contains(dummy60.getId())) ;
			this.mainRepo.destroy(dummy60.getId()) ;
			Assert.assertFalse(this.mainRepo.contains(60)) ;
		}
		catch (NotFoundException e1)
		{
			Assert.fail("dummy not inserted") ;
		}

		try
		{
			this.mainRepo.destroy(dummy60.getId()) ;
		}
		catch (final Exception e)
		{
			// pass
		}
		Assert.assertFalse(this.mainRepo.contains(dummy60.getId())) ;
	}

	/**
	 * Test method for {@link persistence.repository.Repository#flush()}.
	 */
	public void testFlush()
	{
		try
		{
			final Vector<Dummy> dummies = this.makeDummies(80, 88) ;
			this.mainRepo.flush() ;
			PersistenceController persistence = RepositoryTest.newPersistence(PersistenceController.LOAD) ;
			DummyRepository additionalRepo = new DummyRepository(persistence) ;
			Collection<Dummy> dummiesFromAdditionalRepo = additionalRepo.getAll() ;
			Assert.assertTrue(dummiesFromAdditionalRepo.containsAll(dummies)) ;
			additionalRepo.close() ;
		}
		catch (CorruptDataException e)
		{
			Assert.fail("cannot load from database") ;
		}
	}

	/**
	 * Test method for {@link persistence.repository.Repository#get(long)}.
	 */
	public void testGet()
	{
		try
		{
			final Vector<Dummy> dummies = this.makeDummies(70, 75) ;

			for (final Dummy dummy: dummies)
			{
				Dummy dummyFromRepo ;
				dummyFromRepo = this.mainRepo.get(dummy.getId()) ;
				Assert.assertSame(dummy, dummyFromRepo) ;
			}
		}
		catch (final NotFoundException e)
		{
			Assert.fail("object inserted but not found") ;
		}

		try
		{
			this.mainRepo.get(-3) ;
			Assert.fail("exception not thrown") ;
		}
		catch (final NotFoundException e)
		{
			// success
		}
	}

	/**
	 * Test method for {@link persistence.repository.Repository#getAll()}.
	 */
	public void testGetAll()
	{
		final Vector<Dummy> dummies = this.makeDummies(80, 88) ;
		final Collection<Dummy> dummiesFromRepo = this.mainRepo.getAll() ;
		Assert.assertTrue(dummiesFromRepo.containsAll(dummies)) ;
	}

	/**
	 * Test method for {@link persistence.repository.Repository#put(persistence.Persistent)}.
	 */
	public void testPut()
	{
		try
		{
			Dummy dummy = this.mainRepo.create("dummy111") ;
			Dummy dummy2 = this.mainRepo.get(dummy.getId()) ;
			Assert.assertSame(dummy, dummy2) ;
		}
		catch (NotFoundException e)
		{
			Assert.fail("put did not insert to database") ;
		}
	}

	@Override
	protected void setUp() throws Exception
	{
		PersistenceController persistence = RepositoryTest.newPersistence(PersistenceController.BUILD_NEW) ;
		this.mainRepo = new DummyRepository(persistence) ;
	}

	@Override
	protected void tearDown() throws Exception
	{
		this.mainRepo.close() ;
	}

	private Vector<Dummy> makeDummies(final int start, final int end)
	{
		final Vector<Dummy> dummies = new Vector<Dummy>() ;
		for (int i = start ; i < end ; i++)
		{
			dummies.add(this.mainRepo.create("dummy" + i)) ;
		}
		return dummies ;
	}
}
